package asia.hymsk.spring.service.impl;

import asia.hymsk.spring.mapper.UserMapper;
import asia.hymsk.spring.pojo.Response;
import asia.hymsk.spring.pojo.User;
import asia.hymsk.spring.service.UserService;
import asia.hymsk.spring.service.information.email.MailClient;
import asia.hymsk.spring.utils.CryptoUtils;
import asia.hymsk.spring.utils.JwtUtils;
import asia.hymsk.spring.utils.RegexMatchUtils;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.util.RandomUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 花于陌上开
 */
@Service
public class UserServiceImpl implements UserService {
    //初始化
    private final UserMapper userMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final MailClient mailClient;

    public UserServiceImpl(UserMapper userMapper, StringRedisTemplate stringRedisTemplate, MailClient mailClient) {
        this.userMapper = userMapper;
        this.stringRedisTemplate = stringRedisTemplate;
        this.mailClient = mailClient;
    }


    /**
     * 创建验证码并缓存在Redis中
     *
     * @param email 邮箱
     * @return 验证码
     */
    protected String createVerificationCode(String email) throws Exception {
        //检查Redis中验证码获取是否过于频繁，规定24小时内一个手机只能获取5次(间隔1分钟)，过于频繁则拒绝发送
        long timeout = stringRedisTemplate.getExpire(String.format("%s:code", email));
        if (timeout >= 14 * 60) { //请求过于频繁
            throw new Exception("验证码请求过于频繁(请1分钟以后重新获取)");
        }
        String timesString = stringRedisTemplate.opsForValue().get(String.format("%s:times", email));
        if (timesString == null) {  //手机号第一次获取验证码
            stringRedisTemplate.opsForValue().set(String.format("%s:times", email), String.valueOf(1), 24 * 60 * 60, TimeUnit.SECONDS);
        } else {
            int times = Integer.parseInt(timesString);
            if (times < 5) {  //一天之内未获取到5次，可以发送
                stringRedisTemplate.opsForValue().set(String.format("%s:times", email), String.valueOf(++times), 24 * 60 * 60, TimeUnit.SECONDS);
            } else {  //获取次数大于等于5次
                throw new Exception("今日验证码请求次数过多(24小时最多获取5次)");
            }
        }
        //生成验证码
        String newCode = RandomUtil.randomNumbers(6);
        //缓存验证码及存在时间
        stringRedisTemplate.opsForValue().set(String.format("%s:code", email), newCode, 15 * 60, TimeUnit.SECONDS);
        //缓存验证码验证次数及存在时间
        stringRedisTemplate.opsForValue().set(String.format("%s:codetimes", email), String.valueOf(0), 15 * 60, TimeUnit.SECONDS);
        return newCode;
    }

    /**
     * 查询用户名是否存在
     *
     * @param userName 用户名
     * @return 验证状态
     */
    @Override
    public Response userNameIsExist(String userName) {
        //验证用户是否存在
        //用户不存在
        if (userMapper.isExist(userName, null, null) == 0) {
            return Response.error("用户名不存在");
        }
        //用户存在
        return Response.success("用户名已存在");
    }

    /**
     * 进行图灵测试验证
     *
     * @param email 邮箱
     * @return (图片信息和)验证状态
     */
    @Override
    public Response getVerificationTuring(String email) {
        //生成图形验证码
        CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 100, 6, 400);
        //生成code
        circleCaptcha.createCode();
        //将验证码结果与用户信息放入Redis(有效期1分钟)
        stringRedisTemplate.opsForValue().set(String.format("%s:turing", email), circleCaptcha.getCode(), 60, TimeUnit.SECONDS);
        return Response.success("请求成功", circleCaptcha.getImageBase64());
    }

    /**
     * 获取验证码
     *
     * @param email            邮箱
     * @param verificationCode 图灵测试验证码
     * @return 验证状态
     */
    @Override
    public Response sendVerificationCode(String email, String verificationCode) {
        ///验证图形验证码
        String code = stringRedisTemplate.opsForValue().get(String.format("%s:turing", email));
        if (code == null) {
            return Response.error("验证码不存在或已超时，请获取验证码");
        } else if (!Objects.equals(verificationCode, code)) {
            stringRedisTemplate.delete(String.format("%s:turing", email));
            return Response.error("验证码错误，请重新获取验证码");
        }
        String newCode;
        if (RegexMatchUtils.isEmail(email)) {
            //验证邮箱是否存在，若有任意信息存在拒绝发送
            if (userMapper.isExist(null, null, email) != 0) {
                return Response.error("该邮箱已经注册");
            }
            try {
                newCode = createVerificationCode(email);
            } catch (Exception e) {
                return Response.error(e.getMessage());
            }
            //发送邮箱验证码
            try {
                mailClient.sendMessageWithAllNumberCode(email, newCode);
            } catch (Exception e) {
                e.printStackTrace();
                return Response.error("验证码发送失败");
            }
            return Response.success("验证码发送成功");
        }
        return Response.error("邮箱错误");


    }

    /**
     * 进行注册
     *
     * @param userName         用户名
     * @param email            邮箱
     * @param verificationCode 验证码
     * @param password         密码
     * @return 注册状态
     */
    @Override
    public Response userRegister(String userName, String email, String verificationCode, String password) {
        if (userMapper.isExist(userName, email, email) != 0) {
            return Response.error("非法请求，邮箱已经注册");
        }
        //验证码是否存在或超时(15分钟)/超出验证次数(3次)，验证失败拒绝注册
        String phoneCodeTimesString = stringRedisTemplate.opsForValue().get(String.format("%s:codetimes", email));
        if (phoneCodeTimesString == null) {  //验证码不存在
            return Response.error("验证码超时或验证码不存在");
        } else if (Integer.parseInt(phoneCodeTimesString) >= 3) {  //验证次数过多
            return Response.error("验证码验证次数过多");
        }
        //验证验证码是否正确
        stringRedisTemplate.opsForValue().increment(String.format("%s:codetimes", email));
        String code = stringRedisTemplate.opsForValue().get(String.format("%s:code", email));
        if (!Objects.equals(verificationCode, code)) {
            return Response.error("验证码错误，请确认验证码是否输入正确");
        }
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        //进行注册
        try {
            int status;
            if (RegexMatchUtils.isEmail(email)) {
                status = userMapper.registerByEmail(userName, encryptedPassword, email);
            } else {
                return Response.error("参数错误，注册失败");
            }
            if (status == 0) {
                return Response.error("注册失败");
            }
        } catch (org.springframework.dao.DuplicateKeyException e) {
            return Response.error("用户信息重复，注册失败");
        } catch (Exception e) {
            return Response.error("注册失败");
        }
        return Response.success("注册成功");
    }

    /**
     * 重置密码
     *
     * @param email            邮箱
     * @param verificationCode 验证码
     * @param password         密码
     * @return 重置密码状态
     */
    @Override
    public Response userResetPassword(String email, String verificationCode, String password) {
        //验证用户名与email是否存在，若不存在信息存在拒绝发送
        if (userMapper.isExist(null, email, email) == 0) {
            return Response.error("非法请求，邮箱未注册");
        }
        //验证码是否存在或超时(15分钟)/超出验证次数(3次)，验证失败拒绝注册
        String phoneCodeTimesString = stringRedisTemplate.opsForValue().get(String.format("%s:codetimes", email));
        if (phoneCodeTimesString == null) {  //验证码不存在
            return Response.error("验证码超时或验证码不存在");
        } else if (Integer.parseInt(phoneCodeTimesString) >= 3) {  //验证次数过多
            return Response.error("验证码验证次数过多");
        }
        //验证验证码是否正确
        stringRedisTemplate.opsForValue().increment(String.format("%s:codetimes", email));
        String code = stringRedisTemplate.opsForValue().get(String.format("%s:code", email));
        if (!Objects.equals(verificationCode, code)) {
            return Response.error("验证码错误，请确认验证码是否输入正确");
        }
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        //进行修改密码
        try {
            int status;
            if (RegexMatchUtils.isEmail(email)) {
                status = userMapper.updatePasswordByEmail(email, encryptedPassword);
            } else {
                return Response.error("参数错误，重置密码失败");
            }
            if (status == 0) {
                return Response.error("重置密码失败");
            }
        } catch (Exception e) {
            return Response.error("重置密码失败");
        }
        return Response.success("重置密码成功");
    }

    /**
     * 登录
     *
     * @param parameter 用户名/邮箱
     * @param password  密码
     * @return 登录状态
     */
    @Override
    public Response login(String parameter, String password) {
        User user;
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        if (RegexMatchUtils.isEmail(parameter)) { //匹配为邮箱
            user = userMapper.selectByEmail(parameter);
        } else { //匹配为用户名
            user = userMapper.selectByName(parameter);
        }
        if (user == null) {
            return Response.error("用户不存在，请检查输入是否有误");
        }
        if (Objects.equals(encryptedPassword, user.getPassword())) {
            return Response.success("登录成功", JwtUtils.createJwt(user.getUserName()));
        }
        return Response.error("密码错误");
    }

    /**
     * 注册后门
     *
     * @param userName 用户名
     * @param email    邮箱
     * @param password 密码
     * @return 注册状态
     */
    public Response doRegister(String userName, String email, String password) {
        if (userMapper.isExist(userName, email, email) != 0) {
            return Response.error("非法请求，邮箱已经注册");
        }
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        //进行注册
        try {
            int status;
            if (RegexMatchUtils.isEmail(email)) {
                status = userMapper.registerByEmail(userName, encryptedPassword, email);
            } else {
                return Response.error("参数错误，注册失败");
            }
            if (status == 0) {
                return Response.error("注册失败");
            }
        } catch (org.springframework.dao.DuplicateKeyException e) {
            return Response.error("用户信息重复，注册失败");
        } catch (Exception e) {
            return Response.error("注册失败");
        }
        return Response.success("注册成功");
    }

}
